package com.zc.mall.service.impl;

import com.google.gson.Gson;
import com.zc.mall.dao.ProductMapper;
import com.zc.mall.form.CartAddForm;
import com.zc.mall.form.CartUpdateForm;
import com.zc.mall.pojo.Cart;
import com.zc.mall.pojo.Product;
import com.zc.mall.service.ICartService;
import com.zc.mall.vo.CartProductVo;
import com.zc.mall.vo.CartVo;
import com.zc.mall.vo.ResponseVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.zc.mall.enums.ProductStatusEnum.ON_SALE;
import static com.zc.mall.enums.ResponseEnum.*;

/**
 * @author 曾川
 * @version 1.0.0
 * @ClassName CartServiceImpl.java
 * @Description 购物车实现类
 * @createTime 2021年06月08日 10:27:00
 */
@Service
public class CartServiceImpl implements ICartService {

    private final static String CART_REDIS_KEY_TEMPLATE = "cart_%d";

    private Gson gson = new Gson();

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 添加购物车
     *
     * @param uid
     * @return
     */
    @Override
    public ResponseVo<CartVo> add(Integer uid, CartAddForm form) {
        Integer quantity = 1;
        Product product = productMapper.selectByPrimaryKey(form.getProductId());
        //商品是否存在
        if (product == null) {
            return ResponseVo.error(PRODUCT_NOT_EXIST);
        }
        //商品是否正常发售
        if (!product.getStatus().equals(ON_SALE.getCode())) {
            return ResponseVo.error(PRODUCT_OFF_SALE_OR_DELETE);
        }
        //商品库存是否充足
        if (product.getStock() <= 0) {
            return ResponseVo.error(PRODUCT_STOCK_ERROR);
        }
        //写入redis
        //key: cart_1
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);

        //购物车判断
        Cart cart;
        String value = opsForHash.get(redisKey, String.valueOf(product.getId()));
        if (StringUtils.isEmpty(value)) {
            //若没有该商品，则新增
            cart = new Cart(product.getId(),quantity,form.getSelected());
        }else{
            //若有则数量+1
            cart = gson.fromJson(value, Cart.class);//反序列化
            cart.setQuantity(cart.getQuantity() + quantity);
        }

        opsForHash.put(redisKey, String.valueOf(product.getId()), gson.toJson(cart));


        return list(uid);
    }

    /**
     * 购物车列表
     *
     * @param uid
     * @return
     */
    @Override
    public ResponseVo<CartVo> list(Integer uid) {

        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
        Map<String, String> entries = opsForHash.entries(redisKey);//获取购物车和的所有键值对

        boolean selectAll = true;//全选标志
        Integer cartTotalQuantity = 0;//购物车总数
        BigDecimal cartTotalPrice = BigDecimal.ZERO;//购物车总价格
        List<CartProductVo> cartProductVoList = new ArrayList<>();
        CartVo cartVo = new CartVo();
        //遍历键值对
        for (Map.Entry<String, String> entry : entries.entrySet()) {
            Integer productId = Integer.valueOf(entry.getKey());//键
            Cart cart = gson.fromJson(entry.getValue(), Cart.class);//值，json转实体

            //TODO 优化，用mysql  in语句
            Product product = productMapper.selectByPrimaryKey(productId);
            if (product != null) {
                CartProductVo cartProductVo = new CartProductVo(productId,
                        cart.getQuantity(),
                        product.getName(),
                        product.getSubtitle(),
                        product.getMainImage(),
                        product.getPrice(),
                        product.getStatus(),
                        //总价格 = 价格 * 数量
                        product.getPrice().multiply(BigDecimal.valueOf(cart.getQuantity())),
                        product.getStock(),
                        cart.getProductSelected());
                //将vo添加到返回list里
                cartProductVoList.add(cartProductVo);
                if (!cart.getProductSelected()) {
                    selectAll = false;
                }
                //只计算选中的总价
                if (cart.getProductSelected()) {
                    cartTotalPrice = cartTotalPrice.add(cartProductVo.getProductTotalPrice());
                }
            }
            cartTotalQuantity += cart.getQuantity();
        }

        cartVo.setSelectedAll(selectAll);
        cartVo.setCartTotalPrice(cartTotalPrice);
        cartVo.setCartTotalQuantity(cartTotalQuantity);
        cartVo.setCartProductVoList(cartProductVoList);
        return ResponseVo.success(cartVo);
    }

    /**
     * 购物车更新
     *
     * @param uid
     * @param productId
     * @param form
     * @return
     */
    @Override
    public ResponseVo<CartVo> update(Integer uid, Integer productId, CartUpdateForm form) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
        //购物车判断
        String value = opsForHash.get(redisKey, String.valueOf(productId));
        if (StringUtils.isEmpty(value)) {
            //若没有该商品，报错
            return ResponseVo.error(CART_PRODUCT_NOT_EXIST);
        }
        //若有则更新
        Cart cart = gson.fromJson(value, Cart.class);//反序列化
        if (form.getQuantity() != null && form.getQuantity() >= 0) {
            cart.setQuantity(form.getQuantity());
        }
        if (form.getSelected() != null) {
            cart.setProductSelected(form.getSelected());
        }
        //最后更新购物车
        opsForHash.put(redisKey,String.valueOf(productId),gson.toJson(cart));
        return list(uid);
    }

    /**
     * 删除购物车里的商品
     *
     * @param uid
     * @param productId
     * @return
     */
    @Override
    public ResponseVo<CartVo> delete(Integer uid, Integer productId) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
        //购物车判断
        String value = opsForHash.get(redisKey, String.valueOf(productId));
        if (StringUtils.isEmpty(value)) {
            //若没有该商品，报错
            return ResponseVo.error(CART_PRODUCT_NOT_EXIST);
        }
        //删除键值
        opsForHash.delete(redisKey,String.valueOf(productId));
        return list(uid);
    }

    /**
     * 商品全选
     *
     * @param uid
     * @return
     */
    @Override
    public ResponseVo<CartVo> selectAll(Integer uid) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);

        //遍历每个商品，并选中，再设置返回
        for (Cart cart : listForCart(uid)) {
            cart.setProductSelected(true);
            opsForHash.put(redisKey,
                    String.valueOf(cart.getProductId()),
                    gson.toJson(cart));
        }
        return list(uid);
    }

    /**
     * 商品全不选
     *
     * @param uid
     * @return
     */
    @Override
    public ResponseVo<CartVo> unSelectAll(Integer uid) {

        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);

        //遍历每个商品，并选中，再设置返回
        for (Cart cart : listForCart(uid)) {
            cart.setProductSelected(false);
            opsForHash.put(redisKey,
                    String.valueOf(cart.getProductId()),
                    gson.toJson(cart));
        }
        return list(uid);
    }

    /**
     * 购物车商品总数
     *
     * @param uid
     * @return
     */
    @Override
    public ResponseVo<Integer> sum(Integer uid) {
        //使用stream流
        Integer sum = listForCart(uid).stream()
                .map(Cart::getQuantity)
                .reduce(0, Integer::sum);
        return ResponseVo.success(sum);
    }


    public List<Cart> listForCart(Integer uid) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
        Map<String, String> entries = opsForHash.entries(redisKey);

        List<Cart> cartList = new ArrayList<>();
        for (Map.Entry<String, String> entry : entries.entrySet()) {
            cartList.add(gson.fromJson(entry.getValue(),Cart.class));
        }
        return cartList;
    }
}
